package com.wt.admin.service.image.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.wt.admin.code.image.ImageClassification3100;
import com.wt.admin.config.ConstVar;
import com.wt.admin.config.prop.IndexProp;
import com.wt.admin.config.socket.WebSocketSessionManager;
import com.wt.admin.domain.dto.image.ImageClassificationDTO;
import com.wt.admin.domain.dto.image.ImageFeaturesDTO;
import com.wt.admin.domain.dto.image.ImageVideoDTO;
import com.wt.admin.domain.dto.model.ModelListDTO;
import com.wt.admin.domain.entity.image.ImageClassificationEntity;
import com.wt.admin.domain.entity.image.ImageItemEntity;
import com.wt.admin.domain.vo.image.*;
import com.wt.admin.domain.vo.model.ModelListVO;
import com.wt.admin.domain.vo.socket.ProgressVO;
import com.wt.admin.domain.vo.socket.SocketVO;
import com.wt.admin.domain.vo.sys.UserVO;
import com.wt.admin.service.file.UploadFileService;
import com.wt.admin.service.image.*;
import com.wt.admin.service.language.ClassificationService;
import com.wt.admin.service.model.ModelListService;
import com.wt.admin.util.AssertUtil;
import com.wt.admin.util.FileUtils;
import com.wt.admin.util.PageUtil;
import com.wt.admin.util.VideoUtil;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.dromara.easyai.yolo.OutBox;
import org.dromara.easyai.yolo.YoloConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.awt.*;
import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;

@Service("imageProxy")
@Slf4j
public class ImageProxyServiceImpl implements ImageProxyService, UploadFileService {

    @Resource
    private ImageClassificationService imageClassificationService;
    @Resource
    private ImageFeaturesService imageFeaturesService;
    @Resource
    private ImageItemService imageItemService;
    @Resource
    private ClassificationService classificationService;
    @Resource
    private ImageTrainingService imageTrainingService;
    @Resource
    private ImageVideoService imageVideoService;
    @Autowired
    private IndexProp indexProp;
    @Resource
    private Executor publicThread;
    @Resource
    private ModelListService modelListService;


    @Override
    public ImageItemService getImageItemService() {
        return imageItemService;
    }

    @Override
    public ImageFeaturesService getImageFeaturesService() {
        return imageFeaturesService;
    }

    @Override
    public ImageClassificationService getImageClassificationService() {
        return imageClassificationService;
    }

    @Override
    public ImageVideoService getImageVideoService() {
        return imageVideoService;
    }

    @Override
    public void init() {
        log.debug("图像初始化开始");
        List<ImageClassificationVO> list = imageClassificationService.list(null);
        list.parallelStream().forEach(i -> imageTrainingService.init(i));
        log.debug("图像初始化完毕");
    }

    @Override
    public List<ImageItemVO> videoByImages(ImageVideoDTO data) {
        return imageItemService.videoByImages(data.getUrl());
    }

    @Transactional
    @Override
    public ImageVideoVO delVideo(List<String> data) {
        // 删除视频
        imageVideoService.delVideo(data);
        imageItemService.delVideoImage(data);
        // 删除本地文件 TODO
        return null;
    }

    @Transactional
    @Override
    public ImageFeaturesVO featuresEdit(ImageFeaturesDTO data) {
        imageFeaturesService.edit(data);
        return null;
    }

    @Override
    public ImageFeaturesListVO featuresList(PageUtil.PageDTO<ImageFeaturesDTO> data) {
        return ImageFeaturesListVO.builder()
                .features(imageFeaturesService.list(data))
                .classs(classificationService.list(null))
                .build();
    }

    @Transactional
    @Override
    public ImageFeaturesVO featuresDel(ImageFeaturesDTO data) {
        imageFeaturesService.del(data);
        return null;
    }

    @Override
    public ImageClassificationVO imageClassificationDel(ImageClassificationDTO data) {
        int count = imageItemService.findByCountImageClassificationId(data.getId());
        AssertUtil.Boolean.isTrue(count > 0, ImageClassification3100.CODE_3104);
        imageClassificationService.del(data);
        return null;
    }

    @Override
    public ImageClassificationListVO list(ImageClassificationDTO data) {
        return ImageClassificationListVO.builder()
                .imageClasss(imageClassificationService.list(data))
                .classs(classificationService.list(null))
                .build();
    }

    @Async("newAsyncExecutor")
    @Transactional
    @Override
    public void imageTraining(ImageClassificationDTO data,UserVO user) {
        try {
            send(data.getTag() + "训练", 0);
            AssertUtil.objIsNull(data.getId(), ImageClassification3100.CODE_3103);
            List<ImageItemEntity> imageItemVOList = imageItemService.findByClassificationId(data.getId());
            int maxFeaturesNum = imageItemVOList.stream()
                    .peek(i -> i.setStatus(ConstVar.Image.STATUS_20))
                    .mapToInt(ImageItemEntity::getFeaturesNum).max().orElse(0);
            data.getYoloConfig().setTypeNub(maxFeaturesNum);
            data.setTraining(true);
            send(data.getTag() + "训练", 30);
            imageClassificationService.edit(data);
            imageTrainingService.training(data, imageItemVOList,(b) ->
                    modelListService.addModel(user.getId(),data.getRemark(),data.getYoloConfig(), ConstVar.ModelType.IMAGE));
            send(data.getTag() + "训练", 90);
            imageItemService.updateBatch(imageItemVOList);
            send(data.getTag() + "训练", 100);
        } catch (Exception e) {
            send(data.getTag() + "训练", -100);
            log.error("训练失败 {}", e.getMessage());
        }
    }

    @SneakyThrows
    @Override
    public String testTraining(MultipartFile file, Integer id) {
        String name = file.getOriginalFilename();
        String path = "test/";
        name = RandomUtil.randomNumbers(12) + (name.contains(".") ? name.substring(name.indexOf(".")) : "");
        String absPath = indexProp.getFiles().getSavePath() + path + name;
        File tempFile = FileUtil.touch(absPath);
        file.transferTo(tempFile);
        ImageClassificationEntity byId = imageClassificationService.byId(id);
        checkImagePixel(Map.of(absPath, file), byId);
        imageTrainingService.testTraining(id, tempFile, indexProp.getFiles().getSavePath() + path + name);
        return indexProp.getFiles().getSource().replace("*", "") + path + "/" + name;
    }

    @SneakyThrows
    @Override
    public String videoTraining(MultipartFile file, Integer id) {
        String name = file.getOriginalFilename();
        String path = "test/";
        String random = RandomUtil.randomNumbers(12);
        name =  random + (name.contains(".") ? name.substring(name.indexOf(".")) : "");
        String m3u8 = random + ".m3u8";
        String oldPath = indexProp.getFiles().getSavePath() + path + name;
        String newPath = indexProp.getFiles().getSavePath() + path + m3u8;
        ImageClassificationEntity byId = imageClassificationService.byId(id);
        VideoUtil.resizeVideoWH(file.getInputStream(), oldPath, byId.getImageWidth().intValue(), byId.getImageHeight().intValue());
        publicThread.execute(() -> {
            try {
                VideoUtil.videoToStream(oldPath, newPath, byId.getImageWidth().intValue(), byId.getImageHeight().intValue(), (img) -> {
                    InputStream inputStream = VideoUtil.bufferedImageToInputStream(img);
                    List<OutBox> outBoxes = imageTrainingService.testTraining(id, inputStream);
                    Graphics2D g2d = img.createGraphics();
                    outBoxes.forEach(i -> g2d.drawRect(i.getX(), i.getY(), i.getWidth(), i.getHeight()));
                    return g2d;
                });
            }catch (Exception e){
                log.error("识别失败 {}",e.getMessage());
            }
        });
        return "/file/"+path + m3u8;
    }

    @SneakyThrows
    @Override
    public String videoTraining(String url, Integer id) {
        String absPath = indexProp.getFiles().getSavePath() + url;
        String name = "test/" + System.currentTimeMillis() + ".m3u8";
        String absPath1 = indexProp.getFiles().getSavePath() + name;
        System.out.println(absPath1);
        ImageClassificationEntity byId = imageClassificationService.byId(id);
        return name;
    }


    @Override
    public ModelListVO test(ModelListDTO data) {
        // 加载模型
        // 测试模型
        return null;
    }

    @Override
    public boolean exec(String path, MultipartFile file, Map<String, String[]> map, UserVO userVO) {
        String classificationId = map.get("classificationId")[0];
        String languageClassificationId = map.get("languageClassificationId")[0];
        Integer videoFrames = null;
        String videoUrl = null;
        String[] videoFramesArray = map.get("videoFrames");
        String[] videoUrlArray = map.get("videoUrl");
        if (ObjectUtil.isNotEmpty(videoFramesArray) && videoFramesArray.length > 0) {
            videoFrames = Integer.valueOf(videoFramesArray[0]);
        }
        if (ObjectUtil.isNotEmpty(videoUrlArray) && videoUrlArray.length > 0) {
            videoUrl = videoUrlArray[0];
        }
        ImageClassificationEntity imageClassificationEntity = imageClassificationService.byId(Integer.valueOf(classificationId));
        checkImagePixel(Map.of(path, file), imageClassificationEntity);
        ImageItemEntity imageItemEntity = new ImageItemEntity();
        imageItemEntity.setImageUrl(path);
        imageItemEntity.setStatus(ConstVar.Image.STATUS_10);
        imageItemEntity.setClassificationId(Integer.valueOf(classificationId));
        imageItemEntity.setLanguageClassificationId(Integer.valueOf(languageClassificationId));
        imageItemEntity.setVideoUrl(videoUrl);
        imageItemEntity.setVideoFrames(videoFrames);
        imageItemService.saveOne(imageItemEntity);
        return true;
    }

    @Transactional
    @Override
    public boolean batchExec(Map<String, MultipartFile> pathMap, Map<String, String[]> map, UserVO userVO) {
        String classificationId = map.get("classificationId")[0];
        String languageClassificationId = map.get("languageClassificationId")[0];
        ImageClassificationEntity imageClassificationEntity = imageClassificationService.byId(Integer.valueOf(classificationId));
        checkImagePixel(pathMap, imageClassificationEntity);
        List<ImageItemEntity> list = pathMap.keySet().stream().map(i -> {
            ImageItemEntity imageItemEntity = new ImageItemEntity();
            imageItemEntity.setImageUrl(i);
            imageItemEntity.setStatus(ConstVar.Image.STATUS_10);
            imageItemEntity.setClassificationId(Integer.valueOf(classificationId));
            imageItemEntity.setLanguageClassificationId(Integer.valueOf(languageClassificationId));
            return imageItemEntity;
        }).toList();
        imageItemService.saveBatchs(list);
        return true;
    }

    private void checkImagePixel(Map<String, MultipartFile> pathMap, ImageClassificationEntity img) {
        pathMap.keySet().forEach(i -> {
            File file = FileUtil.file(FileUtils.getImageAbsURL(i));
            if (!FileUtil.exist(file)) {
                return;
            }
            try {
                Thumbnails.of(file)
                        .size(img.getImageWidth().intValue(), img.getImageHeight().intValue())
                        .keepAspectRatio(false)
                        .outputQuality(1.0)
                        .toFile(file);
            } catch (Exception e) {
                log.error("图片缩放失败 :{}", e.getMessage());
            }
        });

    }

    private void send(String title, int progress) {
        WebSocketSessionManager.sendToAll(new SocketVO<ProgressVO>
                (ConstVar.Socket.PROGRESS, ProgressVO.set(title, progress)).getJson());
    }

}
